package com.zxmzy.scaffold.http.cookie

import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import android.webkit.CookieManager
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.zxmzy.scaffold.http.RequestManager
import okhttp3.Cookie
import okhttp3.HttpUrl


/**
 * @author zhangxiaoming
 * @date 2023/8/24
 * @description
 * Set-Cookie报头包含于Web服务器的响应头（ResponseHeader）中
 * Cookie报头包含在浏览器客户端请求头（ReguestHeader）中
 */
class CookieJar private constructor(
    private val setCookieCache: SetCookieCache,
    private val cookiePersist: CookiePersist,
    private val webCookieManager: CookieManager
) :
    PersistentCookieJar(setCookieCache, cookiePersist) {
    companion object {
        private const val DOMAINS_PREFERENCES = "domains"
        val instance: CookieJar by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            val webCookieManager: CookieManager = CookieManager.getInstance()
            CookieJar(SetCookieCache(), CookiePersist(), webCookieManager)
        }
    }

    @Synchronized
    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
        setCookieCache.addAll(cookies)
        cookiePersist.saveAll(cookies)
        syncToManager(cookies)
    }

    private fun syncToManager(cookies: List<Cookie>) {
        cookies.forEach {
            webCookieManager.setCookie(it.domain, it.toString())
            val sharedPreferences = getSharedPreferences()
            val domains = sharedPreferences.getString(DOMAINS_PREFERENCES, null)
            if (domains.isNullOrEmpty()) {
                webCookieManager.flush()
                return
            }
            try {
                val gson = Gson()
                val set: Set<String>? =
                    gson.fromJson(domains, object : TypeToken<HashSet<String>>() {}.type)
                if (set.isNullOrEmpty()) {
                    return
                }
                // 将sessionId保存到slSessionId
                for (domain in set) {
                    if (it.name == "JSESSIONID") {
                        webCookieManager.setCookie(domain, "slSessionId=${it.value}")
                    }
                }
            } catch (e: Exception) {
                Log.e("syncToManager", "syncCookieToWebManager fail")
            }
        }
        webCookieManager.flush()
    }

    fun updateWebCookie(url: String): Boolean {
        val domain: String = url.split("//")[1]
        if (domain.isEmpty()) return false
        val sharedPreferences = getSharedPreferences()
        val domains = sharedPreferences.getString(DOMAINS_PREFERENCES, null)
        val needUpdate = domains == null || !domains.contains(domain)
        if (!needUpdate) return false
        return try {
            val gson = Gson()
            var set = mutableSetOf<String>()
            if (domains!!.isNotEmpty()) {
                set = gson.fromJson(domains, object : TypeToken<HashSet<String>>() {}.type)
                if (set.isNullOrEmpty()) {
                    set = mutableSetOf()
                }
            }
            set.add(domain)
            val strJson = gson.toJson(set)
            sharedPreferences.edit().putString(DOMAINS_PREFERENCES, strJson).apply()
            return true
        } catch (e: Exception) {
            Log.e("updateWebCookie", "updateWebCookie fail");
            false
        }
    }

    @Synchronized
    fun removeAll(): Boolean {
        webCookieManager.removeAllCookie()
        webCookieManager.flush()
        super.clear()
        return true
    }

    /**
     * 打印webview的cookie
     */
    fun logCookies(url: String?) {
        if (url != null) {
            Log.d("cookie", "logCookies : ${webCookieManager.getCookie(url)}")
        }
    }

    private fun getSharedPreferences(): SharedPreferences {
        val context: Context = RequestManager.get().appContext
        return context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
    }
}